package io.legado.app

import com.script.ScriptBindings
import com.script.rhino.RhinoScriptEngine
import io.legado.app.data.entities.BookChapter
import org.intellij.lang.annotations.Language
import org.junit.Assert
import org.junit.Test

class JsTest {

    @Language("js")
    private val printJs = """
        function print(str, newline) {
            if (typeof(str) == 'undefined') {
                str = 'undefined';
            } else if (str == null) {
                str = 'null';
            } 
            java.lang.System.out.print(String(str));
            if (newline) java.lang.System.out.print("\n");
        }
        function println(str) { 
            print(str, true);
        }
    """.trimIndent()

    /**
     * 测试使用RhinoScriptEngine评估JavaScript表达式并从Map中提取值。
     *
     * 该测试方法通过以下步骤验证从Map中提取值的功能：
     * 1. 创建一个包含键值对的Map。
     * 2. 将Map绑定到ScriptBindings对象中。
     * 3. 使用RhinoScriptEngine评估两段不同的JavaScript代码，分别通过不同方式从Map中提取值。
     * 4. 验证提取的值是否与预期值一致。
     */
    @Test
    fun testMap() {
        // 创建一个HashMap实例，键为"id"，值为"3242532321"
        val map = hashMapOf("id" to "3242532321")

        // 初始化一个ScriptBindings对象，用于存储脚本执行的上下文变量
        val bindings = ScriptBindings()

        // 将map对象添加到bindings中，键为"result"
        bindings["result"] = map

        // 定义一段JavaScript代码，用于从bindings中获取"id"键对应的值
        @Language("js")
        val jsMap = "$=result;id=$.id;id"

        // 评估JavaScript代码并获取结果
        val result = RhinoScriptEngine.eval(jsMap, bindings)

        // 验证结果是否与预期值一致
        Assert.assertEquals("3242532321", result)

        // 定义另一段JavaScript代码，直接从Map中获取"id"键对应的值
        @Language("js")
        val jsMap1 = """result.get("id")"""

        // 评估JavaScript代码并获取结果
        val result1 = RhinoScriptEngine.eval(jsMap1, bindings)

        // 验证结果是否与预期值一致
        Assert.assertEquals("3242532321", result1)
    }


    @Test
    fun testFor() {
        val scope = RhinoScriptEngine.run {
            val scope = getRuntimeScope(ScriptBindings())
            eval(printJs, scope)
            scope
        }

        @Language("js")
        val jsFor = """
            let result = 0
            let a=[1,2,3]
            let l=a.length
            for (let i = 0;i<l;i++){
            	result = result + a[i]
                println(i)
            }
            for (let o of a){
            	result = result + o
                println(o)
            }
            for (let o in a){
            	result = result + o
                println(o)
            }
            result
        """.trimIndent()
        val result = RhinoScriptEngine.eval(jsFor, scope)
        Assert.assertEquals("12012", result)
    }

    @Test
    fun testReturnNull() {
        val result = RhinoScriptEngine.eval("null")
        Assert.assertEquals(null, result)
    }

    @Test
    fun testReplace() {
        @Language("js")
        val js = """
            s=result.match(/(.{1,6}?)(第.*)/);
            n=s[2].length-parseInt(6-s[1].length);
            s[2].substr(0,n);
        """.trimIndent()
        val x = RhinoScriptEngine.run {
            val bindings = ScriptBindings()
            bindings["result"] = "筳彩涫第七百一十四章 人头树鮺舦綸"
            eval(js, bindings)
        }
        Assert.assertEquals(x, "第七百一十四章 人头树")
    }


    /**
     * 测试BookChapter类中标题的获取
     *
     * 此测试用例旨在验证通过RhinoScriptEngine执行JavaScript代码获取BookChapter实例的标题属性时，
     * 能够正确返回预期的标题字符串
     */
    @Test
    fun chapterText() {
        // 创建一个BookChapter实例，标题设置为"xxxyyy"
        val chapter = BookChapter(title = "xxxyyy")

        // 初始化一个ScriptBindings对象，用于存储脚本执行所需的变量和对象
        val bindings = ScriptBindings()

        // 将BookChapter实例添加到bindings中，以便在脚本中使用
        bindings["chapter"] = chapter

        // 定义一段JavaScript代码，用于获取chapter对象的title属性
        @Language("js")
        val js = "chapter.title"

        // 使用RhinoScriptEngine执行JavaScript代码，并获取执行结果
        val result = RhinoScriptEngine.eval(js, bindings)

        // 断言执行结果与预期的标题"xxxyyy"一致，以验证标题的正确性
        Assert.assertEquals(result, "xxxyyy")
    }

    @Test
    fun javaListForEach() {
        // 初始化一个整数数组列表
        val list = arrayListOf(1, 2, 3)
        // 创建一个脚本绑定对象，并将列表加入绑定中
        val bindings = ScriptBindings()
        bindings["list"] = list
        // 定义一段JavaScript代码，该代码将遍历列表并计算所有元素的总和
        @Language("js")
        val js = """
            var result = 0
            list.forEach(item => {result = result + item})
            result
        """.trimIndent()
        // 使用Rhino脚本引擎执行JavaScript代码，并获取结果
        val result = RhinoScriptEngine.eval(js, bindings)
        // 断言执行结果等于预期值6.0
        Assert.assertEquals(result, 6.0)
    }

    /**
     * 测试字符串类型的识别
     *
     * 此测试旨在验证RhinoScriptEngine正确评估JavaScript代码，并确定变量的类型为字符串
     * 它通过创建一个空的字符串对象并检查其类型来实现
     */
    @Test
    fun typeofString() {
        // 初始化脚本绑定对象，用于执行JavaScript代码
        val bindings = ScriptBindings()

        // 定义JavaScript代码，检查空字符串对象的类型
        @Language("js")
        val js = """
            s = "" + String()
            typeof s
        """.trimIndent()

        // 使用RhinoScriptEngine评估JavaScript代码，并获取结果
        val result = RhinoScriptEngine.eval(js, bindings)

        // 断言评估结果为"string"，以验证字符串类型的正确性
        Assert.assertEquals(result, "string")
    }

}